<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Gitee Contributions</title>
    <style>
      body {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      #stage {
        width: 1000px;
        height: 800px;
      }
    </style>
  </head>
  <body>
    <script src="https://d3js.org/d3.v5.js"></script>
    <div id="stage"></div>
    <script type="module">
      // 导入文件
      function importTxtFile(filePath, name, charsetType = "GBK") {
        return new Promise((resolve, reject) => {
          const xhr = new XMLHttpRequest();
          xhr.open(
            "GET",
            `${filePath}`, // 要查看文件内容可查看public文件夹内对应文件
            false
          );
          xhr.overrideMimeType(`text/plain; charset=${charsetType}`);
          xhr.send();

          if (xhr.readyState === 4 && xhr.status === 200) {
            const content = xhr.responseText;
            const lines = content.split("\n");
            const jsonData = [];

            for (let line of lines) {
              if (line.trim() === "") {
                continue;
              }

              const [key, value] = line.split("：").map((item) => item.trim());
              //   console.log(key, value);
              const count = Number(key.replace("个贡献", ""));
              const date = new Date(value.replace(/-/g, "/"));
              let color = "#eeeeee";
              if (count > 15) {
                color = "#1e6823";
              } else if (count > 10) {
                color = "#44a340";
              } else if (count > 5) {
                color = "#8cc665";
              } else if (count > 0) {
                color = "#d6e685";
              }
              jsonData.push({ count, date, color });
            }

            resolve(jsonData);
          } else {
            reject();
          }
        });
      }

      const importTxtDataToJSON = (path) => {
        return new Promise((resolve, reject) => {
          const promise = importTxtFile(path);
          promise.then((res) => {
            resolve(res);
          });
        });
      };
      import { Scene } from "https://unpkg.com/spritejs/dist/spritejs.esm.js";
      import {
        Cube,
        Light,
        shaders,
      } from "https://unpkg.com/sprite-extend-3d/dist/sprite-extend-3d.esm.js";

      let cache = null;
      async function getData(toDate = new Date()) {
        if (!cache) {
          cache = await importTxtDataToJSON("./gitee-contribution.txt");
        }

        return cache;
      }

      (async function () {
        const container = document.getElementById("stage");

        const scene = new Scene({
          container,
          displayRatio: 2,
        });

        const layer = scene.layer3d("fglayer", {
          ambientColor: [0.5, 0.5, 0.5, 1],
          camera: {
            fov: 35,
          },
        });
        layer.camera.attributes.pos = [2, 6, 9];
        layer.camera.lookAt([0, 0, 0]);

        const light = new Light({
          direction: [-3, -3, -1],
          color: [1, 1, 1, 1],
        });

        layer.addLight(light);

        const program = layer.createProgram({
          vertex: shaders.GEOMETRY.vertex,
          fragment: shaders.GEOMETRY.fragment,
        });

        const dataset = await getData();
        const max = d3.max(dataset, (a) => {
          return a.count;
        });

        const fragment = `
            precision highp float;
            precision highp int;
            varying vec4 vColor;
            varying vec2 vUv;
            void main() {
                float x = fract(vUv.x * 53.0);
                float y = fract(vUv.y * 7.0);
                x = smoothstep(0.0, 0.1, x) - smoothstep(0.9, 1.0, x);
                y = smoothstep(0.0, 0.1, y) - smoothstep(0.9, 1.0, y);
                gl_FragColor = vColor * (x + y);
            }    
        `;

        const axisProgram = layer.createProgram({
          vertex: shaders.TEXTURE.vertex,
          fragment,
        });

        const ground = new Cube(axisProgram, {
          width: 7.6,
          height: 0.1,
          y: -0.049, // not 0.05 to avoid z-fighting
          depth: 1,
          colors: "rgba(0, 0, 0, 0.1)",
        });

        layer.append(ground);

        /* globals d3 */
        const selection = d3.select(layer);
        const chart = selection
          .selectAll("cube")
          .data(dataset)
          .enter()
          .append(() => {
            return new Cube(program);
          })
          .attr("width", 0.14)
          .attr("depth", 0.14)
          .attr("height", 1)
          .attr("scaleY", 0.001)
          .attr("pos", (d, i) => {
            const x0 = -3.8 + 0.0717 + 0.0015;
            const z0 = -0.5 + 0.05 + 0.0015;
            const x = x0 + 0.143 * Math.floor(i / 7);
            const z = z0 + 0.143 * (i % 7);
            return [x, 0, z];
          })
          .attr("colors", (d, i) => {
            return d.color;
          });

        const linear = d3.scaleLinear().domain([0, max]).range([0, 1.0]);

        chart
          .transition()
          .duration(2000)
          .attr("scaleY", (d, i) => {
            return linear(d.count);
          })
          .attr("y", (d, i) => {
            return 0.5 * linear(d.count);
          });

        layer.setOrbit();

        window.layer = layer;
      })();
    </script>
  </body>
</html>
